package com.teemor.common.idempotentspringbootstarter.proxy;

import com.teemor.common.idempotentspringbootstarter.annotation.IdempotentApi;
import com.teemor.common.idempotentspringbootstarter.cache.KeyCache;
import com.teemor.common.idempotentspringbootstarter.fallback.CallFallback;
import com.teemor.common.idempotentspringbootstarter.handler.MethodHandler;
import com.teemor.common.idempotentspringbootstarter.valid.KeyValidtor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @Description:
 * @author zhoulk
 * @date: 2020/5/12
 */
public class IdempotentProxy implements ApplicationContextAware, InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(IdempotentProxy.class);

    private Class clazz;
    private Object target;
    private Map<Method, MethodHandler> dispatch;
    private KeyCache keyCache;
    private KeyValidtor keyValidtor;
    private ApplicationContext context;


    public IdempotentProxy(Class clazz, Object target, Map<Method, MethodHandler> dispatch) {
        this.clazz = clazz;
        this.target = target;
        this.dispatch = dispatch;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        if ("equals".equals(method.getName())) {
            try {
                Object
                        otherHandler =
                        args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
            }
        } else if ("hashCode".equals(method.getName())) {
            return target.hashCode();
        } else if ("toString".equals(method.getName())) {
            return target.toString();
        }


        return dispatch.get(method).invoke(args);

        Method targetMethod = clazz.getDeclaredMethod(methodName);
        IdempotentApi idempotentApi = targetMethod.getAnnotation(IdempotentApi.class);
        if (idempotentApi == null) {
            return method.invoke(args);
        }
        KeyCache keyCache = null;
        if (idempotentApi.isCache()) {
            keyCache = context.getBean(idempotentApi.cacheClazz());
        }
        KeyValidtor keyValidtor = context.getBean(idempotentApi.validClazz());
        boolean check = false;
        if (keyCache != null) {
            check = keyCache.cacheKey(args);
        }
        if (check && keyValidtor != null) {
            check = keyValidtor.validator(args);
        }
        if (check) {
            return method.invoke(args);
        }
        StringBuilder sb = new StringBuilder();
        sb.append("接口重复访问").append("\n")
                .append("method name:").append(methodName).append("\n")
                .append("args:").append("\n");
        for (Object arg : args) {
            sb.append(arg.toString()).append("\n");
        }
        sb.append("日志打印结束");
        LOGGER.info(sb.toString());
        CallFallback callFallback = context.getBean(idempotentApi.fallback());
        return callFallback.call(args);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}
